﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProcedureList.Library.Views;
using ProcedureList.Library.Domain;
using Roslyn.Compilers.CSharp;

namespace ProcedureList.Library.Presenters
{
    public class ProcedureListPresenter : PresenterBase<IProcedureListView>
    {
        public ProcedureListPresenter(
            IProcedureListView view,
            string code) : base (view)
        {
            this.Code = code;
        }

        public void Load()
        {
            List<MethodDto> methods = this.GetMethods(this.Code).ToList();
            this.View.BindMethods(methods);
        }

        public string Code { get; set; }

        

        public IEnumerable<MethodDto> ParseClass(IEnumerable<TypeDeclarationSyntax> classNodes)
        {
            foreach (var classNode in classNodes)
            {
                foreach (var methodNode in classNode
                    .ChildNodes()
                    .OfType<MethodDeclarationSyntax>())
                {
                    yield return new MethodDto()
                    { 
                        ParentClassName = classNode.Identifier.GetFullText().Trim(),
                        MethodName = methodNode.Identifier.GetFullText().Trim(),
                        MethodType =  MethodType.Procedure,
                        Position = methodNode.Span.Start,
                        // SERIOUS law of demeter violation
                        Declaration = string.Format("{0} {1} ({2})",  methodNode.ReturnType.GetFullText().Trim(), methodNode.Identifier.ValueText,
                            string.Join(", ", methodNode.ParameterList.Parameters.Select(p => p.GetFullText().Trim()).ToArray())).Trim()
                    };
                }

                foreach (var propertyNode in classNode
                    .ChildNodes()
                    .OfType<PropertyDeclarationSyntax>())
                {
                    yield return new MethodDto()
                    { 
                        ParentClassName = classNode.Identifier.GetFullText().Trim(),
                        MethodName = propertyNode.Identifier.GetFullText().Trim(),
                        MethodType =  MethodType.Property,
                        Position = propertyNode.Span.Start,
                        Declaration = string.Format("{0} {1}", propertyNode.Type.GetFullText().Trim(), propertyNode.Identifier.GetFullText().Trim())
                    };
                }
            }
        }
        
        public IEnumerable<MethodDto> GetMethods(string sourceCode)
        {
            SyntaxTree tree = SyntaxTree.ParseCompilationUnit(sourceCode);
            var root = (CompilationUnitSyntax)(tree.Root);
            
            var classNodes = root.DescendentNodes()
                .OfType<ClassDeclarationSyntax>();
            
            var interfaceNodes = root.DescendentNodes()
                .OfType<InterfaceDeclarationSyntax>();

            var enumNodes = root.DescendentNodes()
                .OfType<EnumDeclarationSyntax>();

            List<MethodDto> results = new List<MethodDto>();
            results.AddRange(ParseClass(classNodes).ToList());
            results.AddRange(ParseClass(interfaceNodes).ToList());

           
            foreach (var enumNode in enumNodes)
            {
                foreach (var enumTypeNode in enumNode
                    .ChildNodes()
                    .OfType<EnumMemberDeclarationSyntax>())
                {
                    results.Add(new MethodDto()
                    {
                        ParentClassName = enumNode.Identifier.GetFullText().Trim(),
                        MethodName = enumTypeNode.Identifier.GetFullText().Trim(),
                        MethodType = MethodType.Enum,
                        Position = enumTypeNode.Span.Start,
                        Declaration = string.Format("{0}.{1}", enumNode.Identifier.GetFullText().Trim(), enumTypeNode.Identifier.GetFullText().Trim())
                    });

                }
            }

            results = results.OrderBy(methodDto => methodDto.MethodName).ToList();
            foreach (var method in results)
            {
                yield return method;
            }

            


        }
    }

}
